<?php

/**
 * @interal This is internal implementation for the framework.
 *
 * If there are any methods that should be provided, make them
 * public APIs via helper functions.
 * @author  Codeigniter
 */
class ArrayHelper
{
    /**
     * Searches an array through dot syntax. Supports wildcard searches,
     * like `foo.*.bar`.
     *
     * @used-by dot_array_search()
     *
     * @param string $index The index as dot array syntax.
     *
     * @return array|bool|int|object|string|null
     */
    public static function dotSearch(string $index, array $array)
    {
        return self::arraySearchDot(self::convertToArray($index), $array);
    }

    /**
     * @param string $index The index as dot array syntax.
     *
     * @return list<string> The index as an array.
     */
    private static function convertToArray(string $index): array
    {
        // See https://regex101.com/r/44Ipql/1
        $segments = preg_split(
            '/(?<!\\\\)\./',
            rtrim($index, '* '),
            0,
            PREG_SPLIT_NO_EMPTY
        );

        return array_map(
            static fn ($key) => str_replace('\.', '.', $key),
            $segments
        );
    }

    /**
     * Recursively search the array with wildcards.
     *
     * @used-by dotSearch()
     *
     * @return array|bool|float|int|object|string|null
     */
    private static function arraySearchDot(array $indexes, array $array)
    {
        // If index is empty, returns null.
        if ($indexes === []) {
            return null;
        }

        // Grab the current index
        $currentIndex = array_shift($indexes);

        if (! isset($array[$currentIndex]) && $currentIndex !== '*') {
            return null;
        }

        // Handle Wildcard (*)
        if ($currentIndex === '*') {
            $answer = [];

            foreach ($array as $value) {
                if (! is_array($value)) {
                    return null;
                }

                $answer[] = self::arraySearchDot($indexes, $value);
            }

            $answer = array_filter($answer, static fn ($value) => $value !== null);

            if ($answer !== []) {
                // If array only has one element, we return that element for BC.
                return count($answer) === 1 ? current($answer) : $answer;
            }

            return null;
        }

        // If this is the last index, make sure to return it now,
        // and not try to recurse through things.
        if ($indexes === []) {
            return $array[$currentIndex];
        }

        // Do we need to recursively search this value?
        if (is_array($array[$currentIndex]) && $array[$currentIndex] !== []) {
            return self::arraySearchDot($indexes, $array[$currentIndex]);
        }

        // Otherwise, not found.
        return null;
    }

    /**
     * array_key_exists() with dot array syntax.
     *
     * If wildcard `*` is used, all items for the key after it must have the key.
     */
    public static function dotKeyExists(string $index, array $array): bool
    {
        if (str_ends_with($index, '*') || str_contains($index, '*.*')) {
            throw new InvalidArgumentException(
                'You must set key right after "*". Invalid index: "' . $index . '"'
            );
        }

        $indexes = self::convertToArray($index);

        // If indexes is empty, returns false.
        if ($indexes === []) {
            return false;
        }

        $currentArray = $array;

        // Grab the current index
        while ($currentIndex = array_shift($indexes)) {
            if ($currentIndex === '*') {
                $currentIndex = array_shift($indexes);

                foreach ($currentArray as $item) {
                    if (! array_key_exists($currentIndex, $item)) {
                        return false;
                    }
                }

                // If indexes is empty, all elements are checked.
                if ($indexes === []) {
                    return true;
                }

                $currentArray = self::dotSearch('*.' . $currentIndex, $currentArray);

                continue;
            }

            if (! array_key_exists($currentIndex, $currentArray)) {
                return false;
            }

            $currentArray = $currentArray[$currentIndex];
        }

        return true;
    }

    /**
     * Groups all rows by their index values. Result's depth equals number of indexes
     *
     * @used-by array_group_by()
     *
     * @param array $array        Data array (i.e. from query result)
     * @param array $indexes      Indexes to group by. Dot syntax used. Returns $array if empty
     * @param bool  $includeEmpty If true, null and '' are also added as valid keys to group
     *
     * @return array Result array where rows are grouped together by indexes values.
     */
    public static function groupBy(array $array, array $indexes, bool $includeEmpty = false): array
    {
        if ($indexes === []) {
            return $array;
        }

        $result = [];

        foreach ($array as $row) {
            $result = self::arrayAttachIndexedValue($result, $row, $indexes, $includeEmpty);
        }

        return $result;
    }

    /**
     * Recursively attach $row to the $indexes path of values found by
     * `dot_array_search()`.
     *
     * @used-by groupBy()
     */
    private static function arrayAttachIndexedValue(
        array $result,
        array $row,
        array $indexes,
        bool $includeEmpty
    ): array {
        if (($index = array_shift($indexes)) === null) {
            $result[] = $row;

            return $result;
        }
        $value = self::dotSearch($index, $row);

        if (! is_scalar($value)) {
            $value = '';
        }

        if (is_bool($value)) {
            $value = (int) $value;
        }

        if (! $includeEmpty && $value === '') {
            return $result;
        }

        if (! array_key_exists($value, $result)) {
            $result[$value] = [];
        }

        $result[$value] = self::arrayAttachIndexedValue($result[$value], $row, $indexes, $includeEmpty);

        return $result;
    }

    /**
     * Compare recursively two associative arrays and return difference as new array.
     * Returns keys that exist in `$original` but not in `$compareWith`.
     */
    public static function recursiveDiff(array $original, array $compareWith): array
    {
        $difference = [];

        if ($original === []) {
            return [];
        }

        if ($compareWith === []) {
            return $original;
        }

        foreach ($original as $originalKey => $originalValue) {
            if ($originalValue === []) {
                continue;
            }

            if (is_array($originalValue)) {
                $diffArrays = [];

                if (isset($compareWith[$originalKey]) && is_array($compareWith[$originalKey])) {
                    $diffArrays = self::recursiveDiff($originalValue, $compareWith[$originalKey]);
                } else {
                    $difference[$originalKey] = $originalValue;
                }

                if ($diffArrays !== []) {
                    $difference[$originalKey] = $diffArrays;
                }
            } elseif (is_string($originalValue) && ! array_key_exists($originalKey, $compareWith)) {
                $difference[$originalKey] = $originalValue;
            }
        }

        return $difference;
    }

    /**
     * Recursively count all keys.
     */
    public static function recursiveCount(array $array, int $counter = 0): int
    {
        foreach ($array as $value) {
            if (is_array($value)) {
                $counter = self::recursiveCount($value, $counter);
            }

            $counter++;
        }

        return $counter;
    }

    /**
     * Sorts array values in natural order
     * If the value is an array, you need to specify the $sortByIndex of the key to sort
     *
     * @param list<int|list<int|string>|string> $array
     * @param int|string|null                   $sortByIndex
     */
    public static function sortValuesByNatural(array &$array, $sortByIndex = null): bool
    {
        return usort($array, static function ($currentValue, $nextValue) use ($sortByIndex) {
            if ($sortByIndex !== null) {
                return strnatcmp((string) $currentValue[$sortByIndex], (string) $nextValue[$sortByIndex]);
            }

            return strnatcmp((string) $currentValue, (string) $nextValue);
        });
    }
}

function array_value(array $arr, $key, $default = '') {
	return isset($arr[$key]) ? $arr[$key] : $default;
}

function array_filter_empty($arr) {
	foreach($arr as $k=>$v) {
		if(empty($v)) unset($arr[$k]);
	}
	return $arr;
}

/*
function array_isset_push(&$arr, $key, $value) {
	!isset($arr[$key]) AND $arr[$key] = array();
	$arr[$key][] = $value;
}
*/


function array_addslashes(&$var) {
	if(is_array($var)) {
		foreach($var as $k=>&$v) {
			array_addslashes($v);
		}
	} else {
		if(is_string($var)) $var = addslashes($var);
	}
	return $var;
}

function array_stripslashes(&$var) {
	if(is_array($var)) {
		foreach($var as $k=>&$v) {
			array_stripslashes($v);
		}
	} else {
        if(is_string($var)) $var = stripslashes($var);
	}
	return $var;
}

function array_htmlspecialchars(&$var) {
	if(is_array($var)) {
		foreach($var as $k=>&$v) {
			array_htmlspecialchars($v);
		}
	} else {
		$var = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $var);
	}
	return $var;
}

function array_trim(&$var) {
	if(is_array($var)) {
		foreach($var as $k=>&$v) {
			array_trim($v);
		}
	} else {
		$var = trim($var);
	}
	return $var;
}

// 比较数组的值，如果不相同则保留，以第一个数组为准
function array_diff_value($arr1, $arr2) {
	foreach ($arr1 as $k=>$v) {
		if(isset($arr2[$k]) && $arr2[$k] == $v ) unset($arr1[$k]);
	}
	return $arr1;
}

/*
	$data = array();
	$data[] = array('volume' => 67, 'edition' => 2);
	$data[] = array('volume' => 86, 'edition' => 1);
	$data[] = array('volume' => 85, 'edition' => 6);
	$data[] = array('volume' => 98, 'edition' => 2);
	$data[] = array('volume' => 86, 'edition' => 6);
	$data[] = array('volume' => 67, 'edition' => 7);
	arrlist_multisort($data, 'edition', TRUE);
*/
// 对多维数组排序
function arrlist_multisort($arrlist, $col, $asc = TRUE) {
	$colarr = array();
	foreach($arrlist as $k=>$arr) {
		$colarr[$k] = $arr[$col];
	}
	$asc = $asc ? SORT_ASC : SORT_DESC;
	array_multisort($colarr, $asc, $arrlist);
	return $arrlist;
}

// 对数组进行查找，排序，筛选，支持多种条件排序
function arrlist_cond_orderby($arrlist, $cond = array(), $orderby = array(), $page = 1, $pagesize = 20) {
	$resultarr = array();
	if(empty($arrlist)) return $arrlist;
	
	// 根据条件，筛选结果
	if($cond) {
		foreach($arrlist as $key=>$val) {
			$ok = TRUE;
			foreach($cond as $k=>$v) {
				if(!isset($val[$k])) {
					$ok = FALSE; break;
				}
				if(!is_array($v)) {
					if($val[$k] != $v) {
						$ok = FALSE; break;
					}
				} else {
					foreach($v as $k3=>$v3) {
						if(
							($k3 == '>' && $val[$k] <= $v3) || 
							($k3 == '<' && $val[$k] >= $v3) ||
							($k3 == '>=' && $val[$k] < $v3) ||
							($k3 == '<=' && $val[$k] > $v3) ||
							($k3 == '==' && $val[$k] != $v3) ||
							($k3 == 'LIKE' && stripos($val[$k], $v3) === FALSE)
						)  {
							$ok = FALSE; break 2;
						}
					}
				}
			}
			if($ok) $resultarr[$key] = $val;
		}
	} else {
		$resultarr = $arrlist;
	}

	if($orderby) {
		
		// php 7.2 deprecated each()
		//list($k, $v) = each($orderby);
		
		$k = key($orderby);
		$v = current($orderby);
		
		$resultarr = arrlist_multisort($resultarr, $k, $v == 1);
	}

	$start = ($page - 1) * $pagesize;

	$resultarr = array_assoc_slice($resultarr, $start, $pagesize);
	return $resultarr;
}

function array_assoc_slice($arrlist, $start, $length = 0) {
	if(isset($arrlist[0])) return array_slice($arrlist, $start, $length);
	$keys = array_keys($arrlist);
	$keys2 = array_slice($keys, $start, $length);
	$retlist = array();
	foreach($keys2 as $key) {
		$retlist[$key] = $arrlist[$key];
	}

	return $retlist;
}


// 从一个二维数组中取出一个 key=>value 格式的一维数组
function arrlist_key_values($arrlist, $key, $value = NULL, $pre = '') {
	$return = array();
	if($key) {
		foreach((array)$arrlist as $k=>$arr) {
			$return[$pre.$arr[$key]] = $value ? $arr[$value] : $k;
		}
	} else {
		foreach((array)$arrlist as $arr) {
			$return[] = $arr[$value];
		}
	}
	return $return;
}

/* php 5.5:
function array_column($arrlist, $key) {
	return arrlist_values($arrlist, $key);
}
*/

// 从一个二维数组中取出一个 values() 格式的一维数组，某一列key
function arrlist_values($arrlist, $key) {
	if(!$arrlist) return array();
	$return = array();
	foreach($arrlist as &$arr) {
		$return[] = $arr[$key];
	}
	return $return;
}

// 从一个二维数组中对某一列求和
function arrlist_sum($arrlist, $key) {
	if(!$arrlist) return 0;
	$n = 0;
	foreach($arrlist as &$arr) {
		$n += $arr[$key];
	}
	return $n;
}

// 从一个二维数组中对某一列求最大值
function arrlist_max($arrlist, $key) {
	if(!$arrlist) return 0;
	$first = array_pop($arrlist);
	$max = $first[$key];
	foreach($arrlist as &$arr) {
		if($arr[$key] > $max) {
			$max = $arr[$key];
		}
	}
	return $max;
}

// 从一个二维数组中对某一列求最大值
function arrlist_min($arrlist, $key) {
	if(!$arrlist) return 0;
	$first = array_pop($arrlist);
	$min = $first[$key];
	foreach($arrlist as &$arr) {
		if($min > $arr[$key]) {
			$min = $arr[$key];
		}
	}
	return $min;
}


// 将 key 更换为某一列的值，在对多维数组排序后，数字key会丢失，需要此函数
function arrlist_change_key($arrlist, $key = '', $pre = '') {
	$return = array();
	if(empty($arrlist)) return $return;
	foreach($arrlist as &$arr) {
		if(empty($key)) {
			$return[] = $arr;
		} else {
			$return[$pre.''.$arr[$key]] = $arr;
		}
	}
	//$arrlist = $return;
	return $return;
}

// 保留指定的 key
function arrlist_keep_keys($arrlist, $keys = array()) {
	!is_array($keys) AND $keys = array($keys);
	foreach($arrlist as &$v) {
		$arr = array();
		foreach($keys as $key) {
			$arr[$key] = isset($v[$key]) ? $v[$key] : NULL;
		}
		$v = $arr;
	}
	return $arrlist;
}

// 根据某一列的值进行 chunk
function arrlist_chunk($arrlist, $key) {
	$r = array();
	if(empty($arrlist)) return $r;
	foreach($arrlist as &$arr) {
		!isset($r[$arr[$key]]) AND $r[$arr[$key]] = array();
		$r[$arr[$key]][] = $arr;
	}
	return $r;
}

/**
 * 使用点表示法在数组中搜索特定键,并允许使用通配符 ‘*’。<br/>
 * 可以使用星号作为通配符来替换任何段。找到时,它将搜索所有子节点直到找到它。如果你不知道值,或如果你的值具有数值索引,这很方便
 *
 * @param string $index 用点表示法描述如何在数组中搜索的字符串;如果数组键包含点,则可以用反斜杠转义键
 * @param array $array 要搜索的数组
 * @return array|bool|int|object|string|null 在数组中找到的值,如果没有找到则为 null
 * @uses  示例：<br/>
 * $data=['foo'=>['buzz'=>['fizz'=>11,],'bar'=>['baz'=>23]]];<br/>
 * $fizz = dot_array_search('foo.buzz.fizz', $data);//Returns: 11<br/>
 * $baz = dot_array_search('foo.bar.baz', $data);//Returns: 23<br/>
 * $baz = dot_array_search('foo.*.baz', $data);//Returns: 23
 * @author  Codeigniter
 */
function dot_array_search(string $index, array $array)
{
    return ArrayHelper::dotSearch($index, $array);
}

/**
 * 按索引值将数据行分组在一起。返回的数组的深度等于作为参数传递的索引数。
 *
 * @param array $array        数据行（可能来自查询结果）
 * @param array $indexes      要按索引值分组的索引。遵循点语法
 * @param bool  $includeEmpty 如果为 true，则不过滤掉null和''值
 *
 * @return array 按索引值分组的数组
 * @link https://codeigniter.org.cn/user_guide/helpers/array_helper.html#array_group_by
 * @author  Codeigniter
 */
function array_group_by(array $array, array $indexes, bool $includeEmpty = false): array
{
    return ArrayHelper::groupBy($array, $indexes, $includeEmpty);
}
/**
 * 从一个深度不确定的数组返回具有键值的元素的值
 *
 * @param int|string $key 目标键
 * @param array $array 要搜索的数组
 * @return array|bool|float|int|object|string|null 在数组中找到的值,如果没有找到则为 null
 * @author  Codeigniter
 */
function array_deep_search($key, array $array)
{
    if (isset($array[$key])) {
        return $array[$key];
    }

    foreach ($array as $value) {
        if (is_array($value) && ($result = array_deep_search($key, $value))) {
            return $result;
        }
    }

    return null;
}

/**
 * 以分层方式根据一个或多个键的值对多维数组的元素进行排序。<br/>
 * 该方法也可以处理对象数组,它将检测每个嵌套级别中的元素类型并相应处理。
 *
 * Both arrays of objects and arrays of array can be sorted.
 *
 * @param array $array       要排序的数组(通过引用传递)
 * @param array $sortColumns 要排序的数组键及各自的 PHP 排序标志的关联数组
 * @return bool 排序是否成功
 * @example:
 *     array_sort_by_multiple_keys($players, [
 *         'team.hierarchy' => SORT_ASC,
 *         'position'       => SORT_ASC,
 *         'name'           => SORT_STRING,
 *     ]);
 *
 * The '.' dot operator in the column name indicates a deeper array or
 * object level. In principle, any number of sublevels could be used,
 * as long as the level and column exist in every array element.
 *
 * For information on multi-level array sorting, refer to Example #3 here:
 * https://www.php.net/manual/de/function.array-multisort.php
 *
 * @uses 示例：<br/>
 * 从某个模型的 find() 函数返回以下数组:<br/>
 * $players=[0=>['name'=>'John','team_id'=>2,'position'=>3,'team'=>['id'=>1,'order'=>2,],],1=>['name'=>'Maria','team_id'=>5,'position'=>4,'team'=>['id'=>5,'order'=>1,],],2=>['name'=>'Frank','team_id'=>5,'position'=>1,'team'=>['id'=>5,'order'=>1,],],];<br/>
 * 现在按两个键对该数组进行排序。请注意,该方法支持使用点表示法访问更深层数组级别中的值,但不支持通配符:<br/>
 * array_sort_by_multiple_keys($players, ['team.order' => SORT_ASC,'position'   => SORT_ASC,]);<br/>
 * 现在 $players 数组已根据每个球员 ‘team’ 子数组中的 ‘order’ 值排序。如果对几个球员此值相等,则这些球员将根据其 ‘position’ 进行排序。结果数组为:<br/>
 * $players=[0=>['name'=>'Frank','team_id'=>5,'position'=>1,'team'=>['id'=>5,'order'=>1,],],1=>['name'=>'Maria','team_id'=>5,'position'=>4,'team'=>['id'=>5,'order'=>1,],],2=>['name'=>'John','team_id'=>2,'position'=>3,'team'=>['id'=>1,'order'=>2,],],];<br/>
 * 同样,该方法也可以处理对象数组。在上面的示例中,每个 ‘player’ 都可能由一个数组表示,而 ‘teams’ 是对象。
 * @author  Codeigniter
 */
function array_sort_by_multiple_keys(array &$array, array $sortColumns): bool
{
    // Check if there really are columns to sort after
    if ($sortColumns === [] || $array === []) {
        return false;
    }

    // Group sorting indexes and data
    $tempArray = [];

    foreach ($sortColumns as $key => $sortFlag) {
        // Get sorting values
        $carry = $array;

        // The '.' operator separates nested elements
        foreach (explode('.', $key) as $keySegment) {
            // Loop elements if they are objects
            if (is_object(reset($carry))) {
                // Extract the object attribute
                foreach ($carry as $index => $object) {
                    $carry[$index] = $object->{$keySegment};
                }

                continue;
            }

            // Extract the target column if elements are arrays
            $carry = array_column($carry, $keySegment);
        }

        // Store the collected sorting parameters
        $tempArray[] = $carry;
        $tempArray[] = $sortFlag;
    }

    // Append the array as reference
    $tempArray[] = &$array;

    // Pass sorting arrays and flags as an argument list.
    return array_multisort(...$tempArray);
}

/**
 * 使用点作为键的分隔符,将多维数组展平为单个键值对数组。<br/>
 * 该函数在内部使用此参数来跟踪展平后的键。如果用户将提供初始 $id,它将添加到所有键前面。
 *
 * @param iterable $array 要打平的多维数组
 * @param string   $id    可选的 ID 以添加到外部键前面。内部使用以展平键。
 *
 * @return array 打平的数组
 * @link https://codeigniter.org.cn/user_guide/helpers/array_helper.html#array_flatten_with_dots
 * @author  Codeigniter
 */
function array_flatten_with_dots(iterable $array, string $id = ''): array
{
    $flattened = [];

    foreach ($array as $key => $value) {
        $newKey = $id . $key;

        if (is_array($value) && $value !== []) {
            $flattened = array_merge($flattened, array_flatten_with_dots($value, $newKey . '.'));
        } else {
            $flattened[$newKey] = $value;
        }
    }

    return $flattened;
}
/**
 * 判断数组的维度
 * @param array $array 要判断的数组
 * @return int|mixed
 */
function array_depth(&$array)
{
    if (!is_array($array)) return 0;
    $max_depth = 1;
    foreach ($array as &$value) {
        if (is_array($value)) {
            $depth = array_depth($value) + 1;
            $max_depth = max($max_depth, $depth);
        }
    }
    return $max_depth;
}


/**
 * 数组矩阵转换,即将二维数组的列转为行,以第二维的键名作为新的键名,以第一个数组成员的长度为标准,其余超出者忽略,不足者不处理
 * @param array &$arr2 要转换的二维数组
 * @param bool $refvalue 是否使用值引用,默认为false
 * @return array|false
 */
function &ArrayMatrixConversion(array &$arr2,bool $refvalue=false){
    $ret = false;
    //array_unshift($arr2, null);
    //$arr = call_user_func_array('array_map', $arr2);
    $cur_field = null;
    if(!empty($arr2) && is_array(($cur_field = current($arr2)))){
        if(!is_numeric(key($arr2))){
            //进一步检查值:
            $isAllArr = true;
            foreach ($arr2 as $tk => &$tv){
                if(!is_array($tv)){
                    $isAllArr = false;
                    break;
                }
            }
            //数组矩阵转换:
            $arrCols = count($cur_field);
            if($arrCols>0 && $isAllArr) {
                $ret = array_fill(0,$arrCols,array());//填充空数组
                foreach ($arr2 as $k => &$v) {
                    $pos = 0;
                    foreach ($v as $k2 => &$v2) {
                        if(!$refvalue) $ret[$pos][$k] = $v2;
                        else $ret[$pos][$k] = &$v2;
                        if(++$pos >= $arrCols) break;
                    }
                }
            }
        }
    }
    return $ret;
}

/**
 * 两个多维数组的差集,执行递归操作
 * @param array &$array1
 * @param array &$array2
 * @param bool $strict 是否执行严格比较
 * @return array
 */
function &array_diff_assoc_recursive(array &$array1, array &$array2,bool $strict=true)
{
    $diffarray = array();
    foreach ($array1 as $key => &$value) {
        if (is_array($value)) {//判断数组每个元素是否是数组
            if (!isset($array2[$key])) {//判断第二个数组是否存在key
                $diffarray[$key] = &$value;
            } elseif (!is_array($array2[$key])) {//判断第二个数组key是否是一个数组
                $diffarray[$key] = &$value;
            } else {
                if(count($value) != count($array2[$key])){//先行比较包含元素数量
                    $diffarray[$key] = &$value;
                }
                else{
                    $diff = &array_diff_assoc_recursive($value, $array2[$key],$strict);
                    if (!empty($diff)) {
                        $diffarray[$key] = &$diff;
                    }
                }
            }
        } else {
            $isDiff = true;
            if(array_key_exists($key, $array2)){
                if($strict)
                {
                    if($value === $array2[$key])
                        $isDiff = false;
                }
                elseif($value == $array2[$key])
                    $isDiff = false;
            }
            if($isDiff) $diffarray[$key] = &$value;
        }
    }
    return $diffarray;
}

/**
 * 测试数组是否以字符串为Key,即是否存在有意义的键名
 * @param array &$arr
 * @param string $type 测试键还是测试值,有效值:key/value,0/1,默认为key
 * @param int $max_interval 比较有序数时的最大间隔值,默认为10
 * @param bool $key_ambiguous_assoc 有歧义时是否视为有意义的键名,默认为true
 * @return bool
 */
function is_assoc_array(array &$arr,$type='key',int $max_interval=10,bool $key_ambiguous_assoc=true)
{
    $ret = true;
    $lastid = 0;
    $is_key = ($type =='key' || $type === 0 || $type === '0');
    if(!empty($arr)){
        $moves = 0;
        $is_ambiguous = true;
        foreach($arr as $k => &$v){
            if($is_key) $r = &$k;
            else $r = &$v;
            if(!is_numeric($r)) {
                $is_ambiguous = false;
                break;
            }
            if($r - $lastid > $max_interval) {
                $is_ambiguous = false;
                break;
            }
            $lastid = $r;
            $moves++;
        }
        $arrs = count($arr);
        if($moves == $arrs) $ret = false;
        if(!$ret && $is_ambiguous && $key_ambiguous_assoc)
            $ret = true;
    }
    return $ret;
}
/**
 * 获取指定数值在范围数组的最大边界值
 * @param array &$arr 范围数组,array({开始值}=>{结束值})
 * @param mixed $start_val 指定数值
 * @return int|float
 */
function get_array_boundary(array &$arr,$start_val){
    $ret = false;
    if(!empty($arr) && is_numeric($start_val)){
        foreach($arr as $k => &$v){
            if($ret < $v && $start_val >= $k && $start_val < $v){
                $ret = $v;
            }
        }
    }
    if($ret === false) $ret = $start_val;
    return $ret;
}

/**
 * 以指定的字段值作为分类依据,整理成新的数组<br/>
 * 功能类似于arrlist_chunk与array_flip,但是其二维上是值的一维数组,不会覆盖重复值
 * @param array &$arrlist 要归类整理的数据
 * @param string $key 指定值的字段名称,新数组的Key部分,不指定时为原数组的value
 * @param string $valsel 提取的值的来源字段名称,新数组的Value部分,如果不指定,则为$arrlist的键名
 * @return array 失败返回空数组,正确时返回反转键值的数组,值的部分一定是array
 */
function &array_flip_s(array &$arrlist, string $key=null,string $valsel=null){
    $ret = array();
    if(empty($arrlist)) return $ret;
    $test = current($arrlist);
    $otherVFS = (!empty($valsel));
    $otherKFS = (!empty($key));
    if(!is_array($test)){
        foreach($arrlist as $k=>&$v){
            if(!isset($ret[$v])) $ret[$v] = array();
            $ret[$v][] = $k;
        }
        return $ret;
    }
    if($otherKFS && !isset($test[$key])) return $ret;
    if($otherVFS && !isset($test[$valsel])) return $ret;
    foreach($arrlist as $k=>&$v){
        if(!isset($ret[$v[$key]])) $ret[$v[$key]] = array();
        if($otherVFS)  $ret[$v[$key]][] = $v[$valsel];
        else  $ret[$v[$key]][] = $k;
    }
    return $ret;
}

/**
 * 获取指定层级的数组元素
 * @param array &$arr
 * @param int $level
 * @param bool $ref 是否使用引用构造结果
 * @return array
 */
function &get_node_for_level(array &$arr,int $level,bool $ref=true):array {
    $ret = array();
    if(!empty($arr)){
        if($level > 0){
            if(is_array($arr)) {
                foreach ($arr as $k => &$v) {
                    if($level < 2){
                        if ($ref) $ret[$k] = &$v;
                        else $ret[$k] = $v;
                    }
                    else{
                        $retr = &get_node_for_level($v, $level-1, $ref);
                        if (!empty($retr)) {
                            foreach ($retr as $k2 => &$v2) {
                                if (is_int($k2)) {
                                    if ($ref) $ret[] = &$v2;
                                    else $ret[] = $v2;
                                } else {
                                    if ($ref) $ret[$k2] = &$v2;
                                    else $ret[$k2] = $v2;
                                }
                            }
                        }
                        unset($retr);
                    }
                }
            }
        }
    }
    return $ret;
}


/**
 * 在多维数组中查找指定键
 * @param array $arr
 * @param $key
 * @return mixed|null
 */
function &array_findkey(array &$arr, $key)
{
    $ret = false;
    if (empty($arr) || $key === null) return $ret;
    if (isset($arr[$key])) return $arr[$key];
    $ret = null;
    foreach ($arr as &$arv) {
        if (is_array($arv)) {
            $ret = &array_findkey($arv, $key);
            if ($ret !== null) {
                return $ret;
            }
        }
    }
    return $ret;
}

/**
 * 展开第X级数组
 * @param array $arr 要展开的多维数组
 * @param int $level 展开第几级
 * @return array|bool 返回false或者结果数组的引用
 */
function &array_expand(array &$arr, $level = 2)
{
    $ret = false;
    if (empty($arr) || !is_array($arr) || $level < 1) return $ret;
    if ($level == 1) return $arr;
    $ret = array();
    $level--;
    foreach ($arr as $key1 => &$val1) {
        if ($level == 1 && !is_array($val1)) {
            $ret[$key1] = &$val1;
        } elseif(is_array($val1) && !empty($val1)) {
            $retr = &array_expand($val1, $level);
            if (!empty($retr)) {
                foreach ($retr as $keyx => &$valx) {
                    $ret[$keyx] = &$valx;
                }
            }
        }
    }
    return $ret;
}


?>